package gui;


import gui.markers.RectMarker;
import java.util.LinkedList;
import java.util.ListIterator;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;

import gui.hotspots.HotSpot;
import gui.hotspots.HotSpotManager;
import gui.markers.FigureMarker;
import gui.markers.FigureMarkerFactory;
import java.awt.geom.Point2D;

/**
 * Class that represents the main board to draw figures.
 *
 */
public class Board extends JPanel implements MouseListener,
        MouseMotionListener {
    
    private Dimension size;
    private HotSpot hs;
    private HotSpotManager hsm;
    
    private LinkedList figureList;
    private LinkedList redoList;
    private FigureMarker figureMarker;
    
    private Point dragStart;
    
    private int drawingMode;
    
    /**
     * Creates a new instance of Board
     *
     * @param size the initial size.
     */
    public Board(Dimension size) {
        
        super();
        this.size=size;
        drawingMode = FigureMarkerFactory.SELECT;
        
        setMinimumSize(size);
        setPreferredSize(size);
        setSize(size);
        
        figureList = new LinkedList();
        redoList = new LinkedList();
        
        addMouseListener(this);
        addMouseMotionListener(this);
        hsm = new HotSpotManager();
    }
    
    public void setDrawingMode(int drawingMode)	{
        
        this.drawingMode = drawingMode;
    }
    
    public LinkedList getFigureList() {
        
        return figureList;
    }
    
    private void initCanvas(Graphics2D g) {
        
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(Color.white);
        g.fillRect(0, 0, getWidth(), getHeight());
    }
    
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        paintAll((Graphics2D) g);
    }
    
    private void paintAll(Graphics2D g) {
        
        initCanvas(g);
        FigureMarker fm;
        for (int count = 0; count < figureList.size(); count++)	{
            fm = (FigureMarker) figureList.get(count);
            /* g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                     RenderingHints.VALUE_ANTIALIAS_ON);*/
            fm.drawFigure(g, false);
        }
    }
    
    private void paintAll() {
        
        Graphics2D g = (Graphics2D)getGraphics();
        initCanvas(g);
        //initCanvas((Graphics2D)getGraphics());
        FigureMarker fm;
        for (int count = 0; count < figureList.size(); count++)	{
            fm = (FigureMarker) figureList.get(count);
            //fm.drawFigure(( Graphics2D) getGraphics());
            g = (Graphics2D)getGraphics();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            fm.drawFigure(g, false);
        }
    }
    
    /**
     * Sets an appropriate object as a figureMarker
     * depending on the point of mouse click.
     * @param event mouse click
     */
    private void selectObject(MouseEvent event) {
        
        FigureMarker fm;
        FigureMarker result = figureMarker;
        Point p = new Point((int) event.getX(), (int) event.getY());
        
        ListIterator iterator = figureList.listIterator();
        while (iterator.hasNext()) {
            fm = (FigureMarker) iterator.next();
            
            if (fm.contains(p) && (result != fm))
                result = fm;
        }
        figureMarker = result;
    }
    
    /**
     * Draws the figure while it is dragged.
     * @param event mouse cursor
     */
    private void dragObject(MouseEvent event) {
        
        if ((drawingMode == FigureMarkerFactory.SELECT) &&
                (figureMarker != null)) {
            
            Graphics2D g = (Graphics2D) getGraphics();
            figureMarker.drawFigure(g, true);
            
            double x = (event.getX() - dragStart.getX());
            double y = (event.getY() - dragStart.getY());
            dragStart = event.getPoint();
            
            figureMarker.move(x, y);
            figureMarker.drawFigure(g, true);
        }
    }
    
    /**
     * Selects figure or draws it depending on the drawing mode.
     * @param event mouse click
     */
    public void mousePressed(MouseEvent event) {
        
        if (drawingMode == FigureMarkerFactory.SELECT) {
            dragStart = event.getPoint();
            selectObject(event);
        } else if (drawingMode == FigureMarkerFactory.RESIZE){
            hs = hsm.isAt(event.getPoint());
            
        } else {
            Graphics2D g = (Graphics2D) getGraphics();
            figureMarker = FigureMarkerFactory.createObject(drawingMode);
            if (figureMarker != null)
                figureMarker.mousePressed(event.getX(), event.getY(), g);
        }
        // if we do something we cannot undo an undo
        redoList.clear();
    }
    
    /**
     * Draws the whole board or calls mouseReleased method of the figure
     * being drawn (depending on the drawing mode) when the mouse is released.
     * @see FigureMarker
     * @param event mouse click
     */
    public void mouseReleased(MouseEvent event) {
        
        if (drawingMode == FigureMarkerFactory.SELECT)
            paintAll();
        else {
            Graphics2D g = (Graphics2D) getGraphics();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            if (figureMarker != null) {
                int x = event.getX();
                int y = event.getY();
                if (figureMarker.mouseReleased(x, y, g)) {
                    figureList.add(figureMarker);
                    if (figureMarker instanceof RectMarker) {
                        hsm.add(((RectMarker)figureMarker).getHotSpots());
                    }
                } else
                    figureMarker = null;
            }
        }
    }
    
    /**
     * Calls dragObject method or mouseDragged method of the figure being drawn
     * (depending on the drawing mode) when the mouse is dragged.
     * @see FigureMarker
     * @param event mouse cursor
     */
    public void mouseDragged(MouseEvent event) {
        
        if (drawingMode == FigureMarkerFactory.SELECT)
            dragObject(event);
        else if (drawingMode == FigureMarkerFactory.RESIZE){
            if ( (hs != null) && (figureMarker != null) ) {
                int x =  event.getX();
                int y =  event.getY();
                hs.setPoint(new Point2D.Double(x, y));
                hs.notifyObservers();
                Graphics2D g = (Graphics2D) getGraphics();
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
                figureMarker.drawFigure(g,false);
                //paintAll();
               // if (figureMarker instanceof RectMarker)
                 //   ((RectMarker)figureMarker).mouseDragged(x, y, g);
            }
            
        } else {
            Graphics2D g = (Graphics2D) getGraphics();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            if (figureMarker != null) {
                int x =  event.getX();
                int y =  event.getY();
                figureMarker.mouseDragged(x, y, g);
            }
        }
    }
    
    public void mouseMoved(MouseEvent event)	{}
    public void mouseClicked(MouseEvent event)	{}
    public void mouseEntered(MouseEvent event)	{}
    public void mouseExited(MouseEvent event)	{}
    
    public void setSelectedFigureMarker(int index) {
        
        figureMarker = (FigureMarker) figureList.get(index);
    }
    
    /**
     * Clears the whole board
     *
     */
    public void clearDrawing() {
        
        while (!figureList.isEmpty()) {
            redoList.add(figureList.getLast());
            figureList.removeLast();
        }
        figureMarker = null;
        //repaint();
        paintAll();
    }
    
    /**
     * Draws last cleared figure
     *
     */
    public void redoLastDrawnFigure() {
        
        if (redoList.size() > 0) {
            figureList.add(redoList.getLast());
            redoList.removeLast();
            paintAll();
        }
    }
    
    /**
     * Clears last drawn figure
     *
     */
    public void clearLastDrawnFigure() {
        
        if (figureList.size() > 0) {
            if (figureMarker == figureList.getLast())
                figureMarker = null;
            
            redoList.add(figureList.getLast());
            figureList.removeLast();
            paintAll();
        }
    }
}
